<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>视频循环播放工具</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/ffmpeg/0.11.6/ffmpeg.min.js"></script>
    <style>
      * {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
        font-family: 'SF Pro Display', 'PingFang SC', 'Helvetica Neue', Arial,
          sans-serif;
      }

      :root {
        --primary-bg: #e6e7ee;
        --secondary-bg: #ffffff;
        --shadow-light: #ffffff;
        --shadow-dark: #d1d9e6;
        --accent-color: #667eea;
        --accent-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        --success-color: #4ecdc4;
        --danger-color: #ff6b6b;
        --warning-color: #feca57;
        --text-primary: #2d3748;
        --text-secondary: #718096;
        --glass-bg: rgba(255, 255, 255, 0.25);
        --glass-border: rgba(255, 255, 255, 0.18);
      }

      body {
        background: var(--primary-bg);
        color: var(--text-primary);
        min-height: 100vh;
        padding: 20px;
        display: flex;
        flex-direction: column;
        align-items: center;
        transition: all 0.3s ease;
        animation: pageLoad 0.8s ease;
      }

      @keyframes pageLoad {
        from {
          opacity: 0;
          transform: translateY(20px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      .container {
        width: 100%;
        max-width: 1000px;
        background: var(--primary-bg);
        border-radius: 30px;
        padding: 40px;
        box-shadow: 20px 20px 60px var(--shadow-dark),
          -20px -20px 60px var(--shadow-light);
        margin-top: 20px;
        transition: all 0.3s ease;
        animation: containerFloat 1s ease 0.2s both;
      }

      @keyframes containerFloat {
        from {
          opacity: 0;
          transform: translateY(30px) scale(0.95);
        }
        to {
          opacity: 1;
          transform: translateY(0) scale(1);
        }
      }

      .container:hover {
        box-shadow: 25px 25px 70px var(--shadow-dark),
          -25px -25px 70px var(--shadow-light);
      }

      header {
        text-align: center;
        margin-bottom: 40px;
        padding: 30px;
        background: var(--primary-bg);
        border-radius: 25px;
        box-shadow: 10px 10px 20px var(--shadow-dark),
          -10px -10px 20px var(--shadow-light);
        animation: headerSlide 0.8s ease 0.4s both;
      }

      @keyframes headerSlide {
        from {
          opacity: 0;
          transform: translateY(-30px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      h1 {
        font-size: 3.2rem;
        margin-bottom: 15px;
        background: var(--accent-gradient);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        background-clip: text;
        font-weight: 700;
        animation: titleGlow 3s ease-in-out infinite alternate;
      }

      @keyframes titleGlow {
        from {
          filter: brightness(1);
        }
        to {
          filter: brightness(1.2);
        }
      }

      .subtitle {
        font-size: 1.3rem;
        color: var(--text-secondary);
        margin-bottom: 30px;
        font-weight: 500;
      }

      .panel {
        background: var(--primary-bg);
        border-radius: 25px;
        padding: 30px;
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light);
        transition: all 0.3s ease;
        position: relative;
        overflow: hidden;
        margin-bottom: 30px;
      }

      .panel::before {
        content: '';
        position: absolute;
        top: 0;
        left: -100%;
        width: 100%;
        height: 100%;
        background: linear-gradient(
          90deg,
          transparent,
          rgba(255, 255, 255, 0.1),
          transparent
        );
        transition: left 0.8s;
      }

      .panel:hover::before {
        left: 100%;
      }

      .panel:hover {
        transform: translateY(-5px);
        box-shadow: 16px 16px 32px var(--shadow-dark),
          -16px -16px 32px var(--shadow-light);
      }

      .panel-title {
        font-size: 1.6rem;
        margin-bottom: 25px;
        color: var(--accent-color);
        display: flex;
        align-items: center;
        font-weight: 600;
      }

      .panel-title::before {
        content: attr(data-icon);
        margin-right: 12px;
        font-size: 1.8rem;
        animation: iconBounce 2s ease-in-out infinite;
      }

      @keyframes iconBounce {
        0%,
        100% {
          transform: scale(1);
        }
        50% {
          transform: scale(1.1);
        }
      }

      .file-upload {
        border: 3px dashed var(--accent-color);
        border-radius: 25px;
        padding: 50px 40px;
        text-align: center;
        transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
        margin-bottom: 25px;
        cursor: pointer;
        background: var(--primary-bg);
        box-shadow: inset 8px 8px 16px var(--shadow-dark),
          inset -8px -8px 16px var(--shadow-light);
        position: relative;
        overflow: hidden;
      }

      .file-upload:hover {
        border-color: var(--success-color);
        transform: translateY(-5px) scale(1.02);
        box-shadow: inset 10px 10px 20px var(--shadow-dark),
          inset -10px -10px 20px var(--shadow-light),
          0 15px 40px rgba(102, 126, 234, 0.25), 0 5px 15px rgba(0, 0, 0, 0.1);
      }

      .file-upload.dragover {
        border-color: var(--success-color);
        background: rgba(78, 205, 196, 0.05);
        transform: scale(1.05);
        box-shadow: inset 12px 12px 24px var(--shadow-dark),
          inset -12px -12px 24px var(--shadow-light),
          0 20px 50px rgba(78, 205, 196, 0.3);
        animation: dragPulse 1s ease-in-out infinite;
      }

      @keyframes dragPulse {
        0%,
        100% {
          border-color: var(--success-color);
        }
        50% {
          border-color: var(--accent-color);
        }
      }

      .file-upload-content {
        position: relative;
        z-index: 1;
      }

      .file-upload-icon {
        font-size: 3.5rem;
        margin-bottom: 20px;
        display: block;
        animation: iconFloat 3s ease-in-out infinite;
        filter: drop-shadow(0 4px 8px rgba(102, 126, 234, 0.2));
      }

      @keyframes iconFloat {
        0%,
        100% {
          transform: translateY(0);
        }
        50% {
          transform: translateY(-10px);
        }
      }

      .file-upload-text {
        font-size: 1.4rem;
        font-weight: 600;
        color: var(--text-primary);
        margin-bottom: 10px;
        background: var(--accent-gradient);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        background-clip: text;
      }

      .file-upload-subtext {
        font-size: 1.1rem;
        color: var(--text-secondary);
        margin-bottom: 20px;
        font-weight: 500;
      }

      .file-upload-formats {
        font-size: 0.9rem;
        color: var(--accent-color);
        font-weight: 500;
        padding: 8px 16px;
        background: rgba(102, 126, 234, 0.1);
        border-radius: 20px;
        display: inline-block;
        margin-bottom: 20px;
      }

      .file-upload input[type='file'] {
        position: absolute;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        opacity: 0;
        cursor: pointer;
        z-index: 2;
      }

      .video-preview {
        width: 100%;
        background: var(--primary-bg);
        border-radius: 20px;
        overflow: hidden;
        margin-top: 20px;
        box-shadow: inset 10px 10px 20px var(--shadow-dark),
          inset -10px -10px 20px var(--shadow-light);
        transition: all 0.3s ease;
      }

      .video-preview:hover {
        box-shadow: inset 12px 12px 24px var(--shadow-dark),
          inset -12px -12px 24px var(--shadow-light);
      }

      video {
        width: 100%;
        max-height: 400px;
        display: block;
        border-radius: 15px;
      }

      .options-form {
        display: flex;
        gap: 20px;
        margin-bottom: 25px;
        flex-wrap: wrap;
        align-items: flex-end;
      }

      .form-group {
        flex: 1;
        min-width: 150px;
      }

      label {
        display: block;
        margin-bottom: 10px;
        color: var(--text-secondary);
        font-weight: 500;
        font-size: 0.95rem;
      }

      input,
      select {
        width: 100%;
        padding: 14px 18px;
        background: var(--primary-bg);
        border: none;
        border-radius: 15px;
        color: var(--text-primary);
        font-size: 1rem;
        font-weight: 500;
        box-shadow: inset 6px 6px 12px var(--shadow-dark),
          inset -6px -6px 12px var(--shadow-light);
        transition: all 0.3s ease;
      }

      input:focus,
      select:focus {
        outline: none;
        box-shadow: inset 8px 8px 16px var(--shadow-dark),
          inset -8px -8px 16px var(--shadow-light),
          0 0 0 3px rgba(102, 126, 234, 0.2);
      }

      button {
        background: var(--primary-bg);
        color: var(--text-primary);
        border: none;
        padding: 14px 28px;
        border-radius: 15px;
        cursor: pointer;
        font-weight: 600;
        transition: all 0.3s ease;
        font-size: 1rem;
        flex-shrink: 0;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light);
        position: relative;
        overflow: hidden;
      }

      button::before {
        content: '';
        position: absolute;
        top: 0;
        left: -100%;
        width: 100%;
        height: 100%;
        background: linear-gradient(
          90deg,
          transparent,
          rgba(255, 255, 255, 0.2),
          transparent
        );
        transition: left 0.5s;
      }

      button:hover::before {
        left: 100%;
      }

      button:hover {
        transform: translateY(-3px);
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light);
      }

      button:active {
        transform: translateY(0);
        box-shadow: inset 6px 6px 12px var(--shadow-dark),
          inset -6px -6px 12px var(--shadow-light);
      }

      button:disabled {
        opacity: 0.6;
        cursor: not-allowed;
        transform: none;
        box-shadow: 4px 4px 8px var(--shadow-dark),
          -4px -4px 8px var(--shadow-light);
      }

      .btn-primary {
        background: var(--accent-gradient);
        color: white;
        padding: 16px 32px;
        font-size: 1.1rem;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light),
          inset 0 1px 0 rgba(255, 255, 255, 0.2);
      }

      .btn-primary:hover {
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light),
          inset 0 1px 0 rgba(255, 255, 255, 0.2),
          0 0 30px rgba(102, 126, 234, 0.3);
      }

      .btn-success {
        background: linear-gradient(
          135deg,
          var(--success-color) 0%,
          #44a08d 100%
        );
        color: white;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light),
          inset 0 1px 0 rgba(255, 255, 255, 0.2);
      }

      .progress-container {
        margin: 30px 0;
        animation: progressSlide 0.8s ease 0.8s both;
      }

      @keyframes progressSlide {
        from {
          opacity: 0;
          transform: translateY(20px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      .progress-bar {
        height: 16px;
        background: var(--primary-bg);
        border-radius: 20px;
        overflow: hidden;
        margin-bottom: 15px;
        box-shadow: inset 6px 6px 12px var(--shadow-dark),
          inset -6px -6px 12px var(--shadow-light);
        position: relative;
      }

      .progress-fill {
        height: 100%;
        background: var(--accent-gradient);
        width: 0%;
        transition: width 0.3s ease;
        border-radius: 20px;
        box-shadow: 0 2px 10px rgba(102, 126, 234, 0.3);
        position: relative;
        overflow: hidden;
      }

      .progress-fill::after {
        content: '';
        position: absolute;
        top: 0;
        left: -100%;
        width: 100%;
        height: 100%;
        background: linear-gradient(
          90deg,
          transparent,
          rgba(255, 255, 255, 0.3),
          transparent
        );
        animation: progressShine 2s infinite;
      }

      @keyframes progressShine {
        0% {
          left: -100%;
        }
        100% {
          left: 100%;
        }
      }

      .progress-text {
        text-align: center;
        font-size: 1rem;
        color: var(--text-secondary);
        font-weight: 500;
      }

      .progress-log {
        margin-top: 20px;
        background: var(--primary-bg);
        border-radius: 15px;
        padding: 20px;
        box-shadow: inset 8px 8px 16px var(--shadow-dark),
          inset -8px -8px 16px var(--shadow-light);
        max-height: 200px;
        overflow-y: auto;
        font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
        font-size: 0.85rem;
        line-height: 1.4;
        border: 1px solid rgba(102, 126, 234, 0.1);
      }

      .progress-log::-webkit-scrollbar {
        width: 8px;
      }

      .progress-log::-webkit-scrollbar-track {
        background: var(--primary-bg);
        border-radius: 10px;
        box-shadow: inset 4px 4px 8px var(--shadow-dark),
          inset -4px -4px 8px var(--shadow-light);
      }

      .progress-log::-webkit-scrollbar-thumb {
        background: var(--accent-gradient);
        border-radius: 10px;
        box-shadow: 0 2px 6px rgba(102, 126, 234, 0.3);
      }

      .log-entry {
        margin-bottom: 8px;
        padding: 8px 12px;
        background: rgba(102, 126, 234, 0.05);
        border-radius: 8px;
        border-left: 3px solid var(--accent-color);
        color: var(--text-primary);
        font-weight: 500;
        transition: all 0.3s ease;
        animation: logEntrySlide 0.3s ease;
      }

      @keyframes logEntrySlide {
        from {
          opacity: 0;
          transform: translateX(-10px);
        }
        to {
          opacity: 1;
          transform: translateX(0);
        }
      }

      .log-entry:hover {
        background: rgba(102, 126, 234, 0.1);
        transform: translateX(5px);
      }

      .log-entry.highlight {
        background: rgba(78, 205, 196, 0.1);
        border-left-color: var(--success-color);
        animation: logHighlight 0.5s ease;
      }

      @keyframes logHighlight {
        0% {
          background: rgba(78, 205, 196, 0.3);
        }
        100% {
          background: rgba(78, 205, 196, 0.1);
        }
      }

      .log-stats {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
        margin-top: 10px;
        padding: 15px;
        background: rgba(102, 126, 234, 0.05);
        border-radius: 12px;
        border: 1px solid rgba(102, 126, 234, 0.1);
      }

      .log-stat {
        display: flex;
        flex-direction: column;
        align-items: center;
        padding: 8px 12px;
        background: var(--primary-bg);
        border-radius: 8px;
        box-shadow: 4px 4px 8px var(--shadow-dark),
          -4px -4px 8px var(--shadow-light);
        min-width: 80px;
        transition: all 0.3s ease;
      }

      .log-stat:hover {
        transform: translateY(-2px);
        box-shadow: 6px 6px 12px var(--shadow-dark),
          -6px -6px 12px var(--shadow-light);
      }

      .log-stat-label {
        font-size: 0.75rem;
        color: var(--text-secondary);
        font-weight: 600;
        margin-bottom: 4px;
        text-transform: uppercase;
      }

      .log-stat-value {
        font-size: 0.9rem;
        color: var(--accent-color);
        font-weight: 700;
        font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
      }

      .actions {
        display: flex;
        justify-content: center;
        gap: 25px;
        margin-top: 30px;
        animation: actionsSlide 0.8s ease 1s both;
      }

      @keyframes actionsSlide {
        from {
          opacity: 0;
          transform: translateY(20px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      .result-panel {
        text-align: center;
        animation: resultPanelSlide 0.8s ease 1.2s both;
      }

      @keyframes resultPanelSlide {
        from {
          opacity: 0;
          transform: translateY(30px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      .status-message {
        min-height: 35px;
        margin: 20px 0;
        color: var(--accent-color);
        font-weight: 600;
        font-size: 1.1rem;
        transition: all 0.3s ease;
      }

      .status-message.processing {
        animation: statusPulse 1.5s ease-in-out infinite;
      }

      @keyframes statusPulse {
        0%,
        100% {
          opacity: 1;
        }
        50% {
          opacity: 0.6;
        }
      }

      .error-message {
        color: var(--danger-color);
        margin: 20px 0;
        min-height: 35px;
        font-weight: 600;
        font-size: 1.1rem;
        animation: errorShake 0.5s ease;
      }

      @keyframes errorShake {
        0%,
        100% {
          transform: translateX(0);
        }
        25% {
          transform: translateX(-5px);
        }
        75% {
          transform: translateX(5px);
        }
      }

      .preview-section {
        display: flex;
        flex-direction: column;
        gap: 15px;
        margin-top: 20px;
        padding: 25px;
        background: var(--primary-bg);
        border-radius: 25px;
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light);
        border: 2px solid var(--accent-color);
      }

      .preview-title {
        font-size: 1.1rem;
        font-weight: 600;
        color: var(--text-primary);
        display: flex;
        align-items: center;
        gap: 8px;
        margin-bottom: 10px;
      }

      .download-section {
        display: flex;
        flex-direction: column;
        gap: 20px;
        margin-top: 20px;
        padding: 25px;
        background: var(--primary-bg);
        border-radius: 25px;
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light);
        border: 2px solid var(--accent-color);
      }

      .download-item {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 15px;
        background: var(--primary-bg);
        border-radius: 15px;
        box-shadow: 6px 6px 12px var(--shadow-dark),
          -6px -6px 12px var(--shadow-light);
        transition: all 0.3s ease;
        border: 1px solid rgba(102, 126, 234, 0.1);
      }

      .download-item:hover {
        transform: translateY(-1px);
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light);
        border-color: var(--accent-color);
      }

      .download-title {
        font-size: 1rem;
        font-weight: 600;
        color: var(--text-primary);
        display: flex;
        align-items: center;
        gap: 8px;
      }

      .download-size {
        font-size: 0.9rem;
        color: var(--text-secondary);
        font-weight: 500;
        padding: 6px 12px;
        background: rgba(102, 126, 234, 0.1);
        border-radius: 15px;
        border: 1px solid rgba(102, 126, 234, 0.2);
        margin: 0 10px;
      }

      .instructions {
        background: var(--primary-bg);
        border-radius: 25px;
        padding: 30px;
        margin-top: 40px;
        box-shadow: 10px 10px 20px var(--shadow-dark),
          -10px -10px 20px var(--shadow-light);
        animation: instructionsSlide 0.8s ease 1.4s both;
      }

      @keyframes instructionsSlide {
        from {
          opacity: 0;
          transform: translateY(30px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      .instructions h3 {
        color: var(--accent-color);
        margin-bottom: 20px;
        font-size: 1.4rem;
        font-weight: 600;
      }

      .instructions ol {
        padding-left: 25px;
        margin-bottom: 20px;
      }

      .instructions li {
        margin-bottom: 12px;
        line-height: 1.7;
        color: var(--text-secondary);
        font-weight: 500;
      }

      .instructions p {
        color: var(--text-secondary);
        font-style: italic;
        font-weight: 500;
        padding: 15px;
        background: rgba(102, 126, 234, 0.1);
        border-radius: 15px;
        border-left: 4px solid var(--accent-color);
      }

      footer {
        margin-top: 40px;
        text-align: center;
        color: var(--text-secondary);
        font-size: 0.95rem;
        padding: 25px;
        background: var(--primary-bg);
        border-radius: 20px;
        box-shadow: 6px 6px 12px var(--shadow-dark),
          -6px -6px 12px var(--shadow-light);
        animation: footerFade 0.8s ease 1.6s both;
      }

      @keyframes footerFade {
        from {
          opacity: 0;
        }
        to {
          opacity: 1;
        }
      }

      @media (max-width: 768px) {
        .container {
          padding: 20px;
          margin: 10px;
        }

        h1 {
          font-size: 2.5rem;
        }

        .subtitle {
          font-size: 1.1rem;
        }

        .options-form {
          flex-direction: column;
        }

        .form-group {
          min-width: 100%;
        }

        .actions {
          flex-direction: column;
          align-items: center;
        }
      }
    </style>
  </head>
  <body>
    <div class="container">
      <header>
        <h1>🔄 视频循环播放工具</h1>
        <p class="subtitle">将视频文件循环指定次数，支持多种视频格式</p>
      </header>

      <div class="panel">
        <h2 class="panel-title" data-icon="🎬">视频上传</h2>
        <div class="file-upload" id="videoUpload">
          <div class="file-upload-content">
            <span class="file-upload-icon">🎬</span>
            <div class="file-upload-text">点击或拖拽视频文件到此处</div>
            <div class="file-upload-subtext">
              支持拖拽上传，最大文件大小 500MB
            </div>
            <div class="file-upload-formats">
              支持格式：MP4, AVI, MOV, MKV, WMV, FLV, WEBM
            </div>
          </div>
          <input
            type="file"
            id="videoInput"
            accept=".mp4,.avi,.mov,.mkv,.wmv,.flv,.webm"
          />
        </div>

        <div class="video-preview" id="videoPreview" style="display: none">
          <video controls id="videoPlayer"></video>
        </div>
      </div>

      <div class="panel">
        <h2 class="panel-title" data-icon="⚙️">循环设置</h2>
        <div class="options-form">
          <div class="form-group">
            <label for="loopCount">循环次数</label>
            <input
              type="number"
              id="loopCount"
              min="2"
              max="50"
              value="3"
              placeholder="输入循环次数"
            />
          </div>
          <div class="form-group">
            <label for="outputFormat">输出格式</label>
            <select id="outputFormat">
              <option value="mp4">MP4</option>
              <option value="avi">AVI</option>
              <option value="mov">MOV</option>
              <option value="mkv">MKV</option>
              <option value="webm">WEBM</option>
            </select>
          </div>
          <div class="form-group">
            <label for="quality">视频质量</label>
            <select id="quality">
              <option value="high">高质量</option>
              <option value="medium" selected>中等质量</option>
              <option value="low">低质量</option>
            </select>
          </div>
          <button class="btn-primary" id="processBtn" onclick="processVideo()">
            🚀 开始处理
          </button>
        </div>
      </div>

      <div class="panel" id="progressPanel" style="display: none">
        <h2 class="panel-title" data-icon="⏳">处理进度</h2>
        <div class="progress-container">
          <div class="progress-bar">
            <div class="progress-fill" id="progressFill"></div>
          </div>
          <div class="progress-text" id="progressText">准备开始...</div>
        </div>

        <div class="progress-log" id="progressLog" style="display: none">
          <div class="log-stats" id="logStats">
            <div class="log-stat">
              <div class="log-stat-label">帧数</div>
              <div class="log-stat-value" id="frameCount">-</div>
            </div>
            <div class="log-stat">
              <div class="log-stat-label">FPS</div>
              <div class="log-stat-value" id="fpsValue">-</div>
            </div>
            <div class="log-stat">
              <div class="log-stat-label">质量</div>
              <div class="log-stat-value" id="qualityValue">-</div>
            </div>
            <div class="log-stat">
              <div class="log-stat-label">大小</div>
              <div class="log-stat-value" id="sizeValue">-</div>
            </div>
            <div class="log-stat">
              <div class="log-stat-label">时间</div>
              <div class="log-stat-value" id="timeValue">-</div>
            </div>
            <div class="log-stat">
              <div class="log-stat-label">码率</div>
              <div class="log-stat-value" id="bitrateValue">-</div>
            </div>
            <div class="log-stat">
              <div class="log-stat-label">速度</div>
              <div class="log-stat-value" id="speedValue">-</div>
            </div>
          </div>
          <div id="logEntries"></div>
        </div>

        <div class="status-message" id="statusMessage"></div>
        <div class="error-message" id="errorMessage"></div>
      </div>

      <div class="panel result-panel" id="resultPanel" style="display: none">
        <h2 class="panel-title" data-icon="✨">处理结果</h2>
        <div class="preview-section" id="previewSection">
          <div class="preview-title">
            <span>🎬</span>
            <span>循环视频预览</span>
          </div>
          <video controls id="resultVideo"></video>
        </div>
        <div class="download-section" id="downloadSection">
          <div class="download-item">
            <div class="download-title">
              <span>📥</span>
              <span>循环视频文件</span>
            </div>
            <div class="download-size" id="downloadSize">计算中...</div>
            <button class="btn-success" id="downloadBtn">下载文件</button>
          </div>
        </div>
      </div>

      <div class="instructions">
        <h3>📋 使用说明</h3>
        <ol>
          <li>选择要循环播放的视频文件（支持MP4、AVI、MOV等格式）</li>
          <li>设置循环次数（2-50次，默认3次）</li>
          <li>选择输出格式和视频质量</li>
          <li>点击"开始处理"按钮，等待处理完成</li>
          <li>预览循环视频效果，满意后下载文件</li>
        </ol>
        <p>
          💡
          提示：处理时间取决于视频大小和循环次数，请耐心等待。所有处理都在您的浏览器中完成，保护您的隐私安全。
        </p>
      </div>

      <footer>
        <p>
          🔒 所有处理都在本地完成，保护您的隐私 | 💻 基于 FFmpeg WebAssembly
          技术
        </p>
      </footer>
    </div>

    <script>
      // FFmpeg 实例
      let ffmpeg = null
      let isLoaded = false

      // 初始化 FFmpeg
      async function loadFFmpeg() {
        if (isLoaded) return

        try {
          ffmpeg = FFmpeg.createFFmpeg({
            log: true,
          })

          // 设置日志监听器
          ffmpeg.setLogger(({ type, message }) => {
            console.log(`[${type}] ${message}`)
            
            // 解析FFmpeg处理日志
            if (type === 'fferr' && message.includes('frame=')) {
              parseFFmpegProgress(message)
            }
          })

          await ffmpeg.load()
          isLoaded = true
          console.log('FFmpeg 加载成功')
        } catch (error) {
          console.error('FFmpeg 加载失败:', error)
          showError('FFmpeg 加载失败，请刷新页面重试')
        }
      }

      // 页面加载时初始化 FFmpeg
      window.addEventListener('load', loadFFmpeg)

      // 文件上传处理
      const videoInput = document.getElementById('videoInput')
      const videoUpload = document.getElementById('videoUpload')
      const videoPreview = document.getElementById('videoPreview')
      const videoPlayer = document.getElementById('videoPlayer')

      // 拖拽上传
      videoUpload.addEventListener('dragover', (e) => {
        e.preventDefault()
        videoUpload.classList.add('dragover')
      })

      videoUpload.addEventListener('dragleave', () => {
        videoUpload.classList.remove('dragover')
      })

      videoUpload.addEventListener('drop', (e) => {
        e.preventDefault()
        videoUpload.classList.remove('dragover')
        const files = e.dataTransfer.files
        if (files.length > 0) {
          handleVideoFile(files[0])
        }
      })

      videoInput.addEventListener('change', (e) => {
        if (e.target.files.length > 0) {
          handleVideoFile(e.target.files[0])
        }
      })

      // 处理视频文件
      function handleVideoFile(file) {
        // 检查文件大小（500MB限制）
        if (file.size > 500 * 1024 * 1024) {
          showError('文件大小超过500MB限制')
          return
        }

        // 检查文件类型
        const allowedTypes = [
          'video/mp4',
          'video/avi',
          'video/quicktime',
          'video/x-msvideo',
          'video/webm',
        ]
        if (
          !allowedTypes.includes(file.type) &&
          !file.name.match(/\.(mp4|avi|mov|mkv|wmv|flv|webm)$/i)
        ) {
          showError('不支持的视频格式')
          return
        }

        // 显示预览
        const url = URL.createObjectURL(file)
        videoPlayer.src = url
        videoPreview.style.display = 'block'

        // 存储文件信息
        window.currentVideoFile = file

        clearMessages()
      }

      // 处理视频循环
      async function processVideo() {
        if (!window.currentVideoFile) {
          showError('请先选择视频文件')
          return
        }

        if (!isLoaded) {
          showError('FFmpeg 还未加载完成，请稍后再试')
          return
        }

        const loopCount = parseInt(document.getElementById('loopCount').value)
        const outputFormat = document.getElementById('outputFormat').value
        const quality = document.getElementById('quality').value

        if (loopCount < 2 || loopCount > 50) {
          showError('循环次数必须在2-50之间')
          return
        }

        try {
          // 显示进度面板
          showProgress()
          updateProgress(0, '准备处理视频...')

          // 读取文件
          const fileData = await readFileAsArrayBuffer(window.currentVideoFile)
          const inputFileName =
            'input.' + getFileExtension(window.currentVideoFile.name)
          const outputFileName = `output_loop_${loopCount}.${outputFormat}`

          // 写入文件到 FFmpeg
          ffmpeg.FS('writeFile', inputFileName, new Uint8Array(fileData))
          updateProgress(10, '文件上传完成...')

          // 构建 FFmpeg 命令
          let qualityArgs = []
          switch (quality) {
            case 'high':
              qualityArgs = ['-crf', '18']
              break
            case 'medium':
              qualityArgs = ['-crf', '23']
              break
            case 'low':
              qualityArgs = ['-crf', '28']
              break
          }

          // 创建循环视频的命令
          const args = [
            '-stream_loop',
            (loopCount - 1).toString(),
            '-i',
            inputFileName,
            '-c:v',
            'libx264',
            '-c:a',
            'aac',
            ...qualityArgs,
            '-preset',
            'medium',
            outputFileName,
          ]

          updateProgress(20, '开始处理视频循环...')

          // 执行 FFmpeg 命令
          await ffmpeg.run(...args)

          updateProgress(90, '处理完成，准备下载...')

          // 读取输出文件
          const outputData = ffmpeg.FS('readFile', outputFileName)
          const outputBlob = new Blob([outputData.buffer], {
            type: `video/${outputFormat}`,
          })

          // 显示结果
          showResult(outputBlob, outputFileName)
          updateProgress(100, '处理完成！')

          // 清理文件
          ffmpeg.FS('unlink', inputFileName)
          ffmpeg.FS('unlink', outputFileName)
        } catch (error) {
          console.error('处理失败:', error)
          showError('视频处理失败: ' + error.message)
        }
      }

      // 显示进度
      function showProgress() {
        document.getElementById('progressPanel').style.display = 'block'
        document.getElementById('resultPanel').style.display = 'none'
        document.getElementById('processBtn').disabled = true
        document.getElementById('progressLog').style.display = 'block'
      }

      // 更新进度
      function updateProgress(percent, message) {
        document.getElementById('progressFill').style.width = percent + '%'
        document.getElementById(
          'progressText'
        ).textContent = `${percent}% - ${message}`
        document.getElementById('statusMessage').textContent = message
        document.getElementById('statusMessage').className =
          'status-message processing'
      }

      // 解析FFmpeg进度日志
      function parseFFmpegProgress(message) {
        // 解析日志信息：frame=43198 fps=3.7 q=31.0 size=543488kB time=00:12:00.05 bitrate=6183.3kbits/s dup=0 drop=95 speed=0.0609x
        const frameMatch = message.match(/frame=\s*(\d+)/)
        const fpsMatch = message.match(/fps=([\d.]+)/)
        const qMatch = message.match(/q=([\d.]+)/)
        const sizeMatch = message.match(/size=\s*(\d+)kB/)
        const timeMatch = message.match(/time=([\d:.-]+)/)
        const bitrateMatch = message.match(/bitrate=([\d.]+)kbits\/s/)
        const speedMatch = message.match(/speed=([\d.]+)x/)

        if (frameMatch || timeMatch) {
          // 更新日志统计信息
          updateLogStats({
            frame: frameMatch ? frameMatch[1] : '-',
            fps: fpsMatch ? parseFloat(fpsMatch[1]).toFixed(1) : '-',
            quality: qMatch ? parseFloat(qMatch[1]).toFixed(1) : '-',
            size: sizeMatch ? (parseInt(sizeMatch[1]) / 1024).toFixed(1) + 'MB' : '-',
            time: timeMatch ? timeMatch[1] : '-',
            bitrate: bitrateMatch ? bitrateMatch[1] + 'kbps' : '-',
            speed: speedMatch ? speedMatch[1] + 'x' : '-'
          })

          // 添加日志条目
          addLogEntry(message)
        }
      }

      // 更新日志统计信息
      function updateLogStats(stats) {
        document.getElementById('frameCount').textContent = stats.frame
        document.getElementById('fpsValue').textContent = stats.fps
        document.getElementById('qualityValue').textContent = stats.quality
        document.getElementById('sizeValue').textContent = stats.size
        document.getElementById('timeValue').textContent = stats.time
        document.getElementById('bitrateValue').textContent = stats.bitrate
        document.getElementById('speedValue').textContent = stats.speed
      }

      // 添加日志条目
      function addLogEntry(message) {
        const logContainer = document.getElementById('logEntries')
        const logEntry = document.createElement('div')
        logEntry.className = 'log-entry'
        
        const timestamp = new Date().toLocaleTimeString()
        logEntry.innerHTML = `
          <span class="log-time">[${timestamp}]</span>
          <span class="log-content">${message}</span>
        `
        
        logContainer.appendChild(logEntry)
        
        // 保持最新的日志在底部可见
        logContainer.scrollTop = logContainer.scrollHeight
        
        // 限制日志条目数量，避免内存占用过多
        const entries = logContainer.querySelectorAll('.log-entry')
        if (entries.length > 50) {
          entries[0].remove()
        }
      }

      // 显示结果
      function showResult(blob, fileName) {
        const url = URL.createObjectURL(blob)

        // 显示预览
        document.getElementById('resultVideo').src = url
        document.getElementById('resultPanel').style.display = 'block'

        // 设置下载
        const downloadBtn = document.getElementById('downloadBtn')
        downloadBtn.onclick = () => {
          const a = document.createElement('a')
          a.href = url
          a.download = fileName
          a.click()
        }

        // 显示文件大小
        const sizeText = formatFileSize(blob.size)
        document.getElementById('downloadSize').textContent = sizeText

        // 重新启用按钮
        document.getElementById('processBtn').disabled = false
      }

      // 显示错误信息
      function showError(message) {
        document.getElementById('errorMessage').textContent = message
        document.getElementById('processBtn').disabled = false
      }

      // 清除消息
      function clearMessages() {
        document.getElementById('statusMessage').textContent = ''
        document.getElementById('errorMessage').textContent = ''
      }

      // 工具函数
      function readFileAsArrayBuffer(file) {
        return new Promise((resolve, reject) => {
          const reader = new FileReader()
          reader.onload = () => resolve(reader.result)
          reader.onerror = reject
          reader.readAsArrayBuffer(file)
        })
      }

      function getFileExtension(fileName) {
        return fileName.split('.').pop().toLowerCase()
      }

      function formatFileSize(bytes) {
        if (bytes === 0) return '0 Bytes'
        const k = 1024
        const sizes = ['Bytes', 'KB', 'MB', 'GB']
        const i = Math.floor(Math.log(bytes) / Math.log(k))
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
      }
    </script>
  </body>
</html>